#include<stdio.h>
#include <string.h>

#include "SEGGER_RTT.h"

#include "FreeRTOS.h"
#include "task.h"


#include "drv_ft780.h"
#include "misc.h"

#include "base16.h"
#include "base64.h"
#include "encoding.h"

#include "drv_bc26.h"



typedef struct _CT_RECV_BUFFER {
    uint8_t pdata[1024];
    uint32_t len;
    uint32_t pos;
}CT_RECV_BUFFER;

typedef struct _ENV_FT780 {
    CT_RECV_BUFFER *pct_recv_buffer;
}ENV_FT780;

static ENV_FT780 env_ft780 = {
    .pct_recv_buffer = NULL
};

int32_t drv_ft780_ct_recv_buffer_init(void) {
    if (env_ft780.pct_recv_buffer == NULL) {
        env_ft780.pct_recv_buffer = pvPortMalloc(sizeof(CT_RECV_BUFFER));
        if (env_ft780.pct_recv_buffer == NULL) {
            return -1;
        } else {
            env_ft780.pct_recv_buffer->pos = 0;
            env_ft780.pct_recv_buffer->len = sizeof(env_ft780.pct_recv_buffer->pdata);
            return 0;
        }
    } else {
        return 0;
    }
}


int32_t drv_ft780_ct_recv_buffer_reset(void) {
    env_ft780.pct_recv_buffer->pos = 0;
    return 0;
}
bool drv_ft780_is_ct_recv_buffer_empty(void) {
    if (env_ft780.pct_recv_buffer->pos == 0) {
        return true;
    } else {
        return false;
    }
}

int32_t drv_ft780_ct_recv_buffer_deinit(void) {
    if (env_ft780.pct_recv_buffer != NULL) {
        vPortFree(env_ft780.pct_recv_buffer);
    }
    return 0;
}

int32_t drv_ft780_ct_recv_buffer_write(uint8_t *pdata, uint32_t len) {
    if (env_ft780.pct_recv_buffer->pos + len > env_ft780.pct_recv_buffer->len) { //out of buffer
        return -1;
    }
    memcpy(env_ft780.pct_recv_buffer->pdata + env_ft780.pct_recv_buffer->pos, pdata, len);
    env_ft780.pct_recv_buffer->pos += len;
    return 0;
}

int32_t drv_ft780_ct_recv_buffer_read(uint8_t **p2data, uint32_t *plen) {
    *p2data = env_ft780.pct_recv_buffer->pdata;
    *plen = env_ft780.pct_recv_buffer->pos;
    return 0;
}

void drv_ft780_cimi(uint8_t *pdata, uint32_t len) {
    for (; *pdata == ' '; pdata++, len--); //skip space
    if (len > sizeof(env_net.pimei)) {
        NET_ERR("invalid imei length");
        return;
    }
    memcpy(env_net.pimei, pdata, len);
    env_net.pimei[len] = '\0';
    NET_INF("imsi:%s,%d", env_net.pimei, len);
}

void drv_ft780_cgsn(uint8_t *pdata, uint32_t len) {
    for (; *pdata == ' '; pdata++, len--); //skip space
    if (len > sizeof(env_net.pimsi)) {
        NET_ERR("invalid imsi length");
        return;
    }
    memcpy(env_net.pimsi, pdata, len);
    env_net.pimsi[len] = '\0';
    NET_INF("imei:%s,%d", env_net.pimsi, len);
}


void drv_ft780_zversion(uint8_t *pdata, uint32_t len) {
    pdata[len] = '\0';
    NET_INF("version:%s", pdata);
    if (strcasecmp((const char *)(pdata + len - 2), "ct") == 0) {
        env_net.carrier = DRV_NET_CARRIER_CT;
    } else if (strcasecmp((const char *)(pdata + len - 2), "cm") == 0) {
        env_net.carrier = DRV_NET_CARRIER_CM;
    } else {
        env_net.carrier = DRV_NET_CARRIER_MAX;
    }
    NET_INF("carrier is %d", env_net.carrier);
}

void drv_ft780_cereg(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    uint32_t number;
    if (at_match(&bytes, "+u,+u", NULL, &number)) {
        return;
    }
    if ((number == 1) || (number == 5)) {
        msg_at.info = AT_INFO_CEREG_OK;
        if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
            NET_ERR("failed to pass the result");
        }
    }
}

void drv_ft780_zesq(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t sq;
    if (at_match(&bytes, "+d,+d,+d", NULL, NULL, &sq)) {
        return;
    }

    if (sq < 0) {
        sq = 0 - sq;
    }
    if (sq >= 113) {
        sq = 0;
    } else {
        sq = (113 - sq) / 2;
    }
    env_net.sq = sq;
    NET_INF("rssi:%d", sq);
//  env_net.sq = sq + ;
}

//+ZIPSTAT: <Socket id>,< Status >
void drv_ft780_zipstat(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    int32_t socket_id, socket_status;
    if (at_match(&bytes, "+d,+d", &socket_id, &socket_status)) {
        return;
    }
    socket_id = socket_id - 1;
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket id %d", socket_id);
        return;
    }
    if (!BETWEEN11(0, socket_status, 4)) {
        NET_ERR("invalid socket status%d", socket_status);
        return;
    }

    msg_at.value = socket_id;
    if (socket_status == 1) {
        msg_at.info = AT_INFO_SOCKET_OPEN_OK;
        if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
            NET_ERR("failed to pass ");
            return;
        }
    } else if (socket_status == 0) {
        msg_at.info = AT_INFO_SOCKET_CLOSE_OK;
        NET_INF("socket %d was closed", socket_id);
        if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
            NET_ERR("failed to pass ");
            return;
        }
    } else if (socket_status == 2) {
        NET_WAR("send buffer of socket %d is full", socket_id);
    }
}

//+ZIPRECV: <Socket id>,<RemoteIP >,<Remote port>,<Data len>,<Data>
//+ZIPRECV: 1,192.232.33.10,21,10,1234567890
void drv_ft780_ziprecv(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    uint32_t recv_len;
    MSG_RECV msg_recv;
    if (at_match(&bytes, "+d,+s,+u,+u,", &socket_id, NULL, NULL, &recv_len)) {
        return;
    }
    socket_id = socket_id - 1;
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket id %d", socket_id);
        return;
    }
//  bytes.pdata[bytes.len] = '\0';
//  NET_DBG("recev(%d,%d,%d):%s", socket_id - 1, bytes.len, recv_len,bytes.pdata);
    recv_len = recv_len << 1;
    if (recv_len > bytes.len) {
        NET_ERR("out of buffer");
        return;
    }
    recv_len = base16_decode(bytes.pdata, recv_len, bytes.pdata, recv_len);
    if (recv_len == 0) {
        NET_ERR("failed to decode");
        return;
    }
    msg_recv.type = MSG_RECV_SOCKET;
    msg_recv.value = socket_id;
    app_fifo_write(&(env_net.pconns[socket_id].fifo), bytes.pdata, &recv_len);
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_ERR("failed to pass recv");
    }
}


void drv_ft780_zipsend(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    MSG_AT msg_at;
    if (at_match(&bytes, "+d,+u", &socket_id, NULL)) {
        return;
    }
    socket_id = socket_id - 1;
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket id %d", socket_id);
        return;
    }
    msg_at.info = AT_INFO_SOCKET_SEND_OK;
    msg_at.value = socket_id;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass ");
        return;
    }
}

extern void drv_bc26_miplcreate(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplevent(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplobserve(uint8_t *pdata, uint32_t len);
extern void drv_bc26_mipldiscover(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplwrite(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplread(uint8_t *pdata, uint32_t len);

#if 0
void drv_ft780_rad(uint8_t *pdata,uint32_t len){
    BYTES bytes = {.pdata = pdata,.len = len};
    BYTES packet;
    MSG_RECV msg_recv;
    uint32_t is_last_packet;
    if (at_match(&bytes,"+s,+d,+d",&packet,&is_last_packet,NULL)) { //todo:more check
        return;
    }
    if (packet.len < 4) {
        pdata[len] = '\0';
        NET_ERR("invalid packet %s", pdata);
        return;
    }
    packet.pdata = &(packet.pdata[4]);
    packet.len -= 4;
    len = base16_decode(packet.pdata, packet.len, packet.pdata, packet.len); //todo:stream decode
    if (len == 0) {
        NET_ERR("failed to decode by base16");
        return;
    }
    len  = base64_decode(packet.pdata,len,packet.pdata,len);
    if (len == 0) {
        NET_ERR("failed to decode by base64");
        return;
    }
    app_fifo_write(&(env_net.pconns[0].fifo), packet.pdata,&len);
    msg_recv.type = MSG_RECV_SOCKET;
    msg_recv.value = 0;
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_ERR("failed to pass socket msg");
        return;
    }
}
#else
void drv_ft780_rad(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    BYTES packet;
    MSG_RECV msg_recv;
    int32_t is_last_packet;
    if (at_match(&bytes, "+s,+d,+d", &packet, &is_last_packet, NULL)) { //todo:more check
        return;
    }
    if (drv_ft780_ct_recv_buffer_init()) {
        NET_ERR("failed to init ct recv buffer");
        return;
    }
    if (!is_last_packet) {
        if (drv_ft780_ct_recv_buffer_write(packet.pdata, packet.len)) {
            NET_ERR("out of buffer");
            drv_ft780_ct_recv_buffer_reset();
            return;
        }
        return; //do nothing
    }
    //else:last packet
    if (!drv_ft780_is_ct_recv_buffer_empty()) { //not empty
        drv_ft780_ct_recv_buffer_write(packet.pdata, packet.len);
        drv_ft780_ct_recv_buffer_read(&(packet.pdata), &(packet.len));
    }
    drv_ft780_ct_recv_buffer_reset();
    if (packet.len < 4) {
        pdata[len] = '\0';
        NET_ERR("invalid packet %s", pdata);
        return;
    }
    packet.pdata = &(packet.pdata[4]);
    packet.len -= 4;
    len = base16_decode(packet.pdata, packet.len, packet.pdata, packet.len); //todo:stream decode
    if (len == 0) {
        NET_ERR("failed to decode by base16");
        return;
    }
    len  = base64_decode(packet.pdata, len, packet.pdata, len);
    if (len == 0) {
        NET_ERR("failed to decode by base64");
        return;
    }
    app_fifo_write(&(env_net.pconns[0].fifo), packet.pdata, &len);
    msg_recv.type = MSG_RECV_SOCKET;
    msg_recv.value = 0;
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_ERR("failed to pass socket msg");
        return;
    }
}
#endif

int32_t drv_ft780_any(uint8_t *pdata, uint32_t len) {
    return -1;
}
AT_HANDLER drv_ft780_at_handlers[] = {
    AT_HANDLER_GENERAL_DEF("OK", drv_net_result_ok),
    AT_HANDLER_GENERAL_DEF("ERROR", drv_net_result_error),
    AT_HANDLER_GENERAL_DEF("+CIMI:", drv_ft780_cimi),
    AT_HANDLER_GENERAL_DEF("+CGSN:", drv_ft780_cgsn),
    AT_HANDLER_GENERAL_DEF("+ZVERSION:", drv_ft780_zversion),
    AT_HANDLER_GENERAL_DEF("+CEREG:", drv_ft780_cereg),
    AT_HANDLER_GENERAL_DEF("+ZIPSTAT:", drv_ft780_zipstat),
    AT_HANDLER_GENERAL_DEF("+ZIPRECV:", drv_ft780_ziprecv),
    AT_HANDLER_GENERAL_DEF("+ZIPSEND:", drv_ft780_zipsend),
    AT_HANDLER_GENERAL_DEF("+ZESQ:", drv_ft780_zesq),
    //onenet
    AT_HANDLER_GENERAL_DEF("+MIPLCREATE", drv_bc26_miplcreate), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLEVENT", drv_bc26_miplevent), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLOBSERVE", drv_bc26_miplobserve), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLDISCOVER", drv_bc26_mipldiscover), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLWRITE:", drv_bc26_miplwrite),
    AT_HANDLER_GENERAL_DEF("+MIPLREAD", drv_bc26_miplread), //todo:
    //ocean connect(ct)
    AT_HANDLER_GENERAL_DEF("+RAD:", drv_ft780_rad),
    AT_HANDLER_ANY(drv_ft780_any)
};




NET_ERR_CODE drv_ft780_try_to_get_ip(void) {
    uint32_t i;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    vTaskDelay(4000);
    ret = drv_net_write_bytes_raw(1000, 15, TSTRING(uint8_t *,"AT\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    //at+znvset="update_reg",0
    ret = drv_net_write_bytes_raw(1000, 15, TSTRING(uint8_t *,"at+znvset=\"update_reg\",0\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    drv_net_power_on();
    ret = drv_net_write_bytes_raw(1000, 15, TSTRING(uint8_t *,"AT\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    //is sim card ready?
    ret = drv_net_write_bytes_raw(500, 1, TSTRING(uint8_t *,"AT+CPIN?\r"));
    if (ret < 0) {
        NET_ERR("no sim card");
        return NET_ERR_NO_CARD;
    }
    //imei
    ret = drv_net_write_bytes_raw(500, 1, TSTRING(uint8_t *,"AT+CIMI\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    //imsi
    ret = drv_net_write_bytes_raw(500, 1, TSTRING(uint8_t *,"AT+CGSN\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    //enable log
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+ZNVSET=\"uart_at\",255\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    //version
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+ZVERSION?\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    ret = drv_net_write_bytes_raw(3000, 1, TSTRING(uint8_t *,"AT+CPSMS=0\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }

//  ret = drv_net_write_bytes_raw(3000, 1, TSTRING(uint8_t *,"AT+EXTPM=1\r"));
//  if(ret < 0){
//      NET_ERR("module might be broken");
//      return ret;
//  }

    xQueueReset(env_net.qinfo);
    for (i = 40; i > 0; i--) {
        NET_INF("try to get ip%02d...", i);
        ret = drv_net_write_bytes_raw(500, 2, TSTRING(uint8_t * , "AT+CEREG?\r"));
        if (ret < 0) {
            NET_ERR("module might be broken");
            return ret;
        }
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_CEREG_OK) {
                break;
            }
        } else if (i == 1) { //timeout
            NET_ERR("failed to register");
            return NET_ERR_UNKOWN;
        } //else {continue;}
    }
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+ZDTMODE=2\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CPSMS=1,,,\"01000100\",\"00000111\"\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
    //useless udp connect
//  ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+ZIPOPEN=4,1,8.8.8.8,53\r"));
//  if (ret < 0) {
//      NET_ERR("module might be broken");
//      return ret;
//  }
    drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+CGDCONT?\r"));
    NET_INF("ft780 registered successfully");
    drv_net_write_bytes_raw(500, 1, TSTRING(uint8_t * , "AT+ZESQ\r"));
    vTaskDelay(2000);
    return NET_ERR_OK;
}

NET_ERR_CODE drv_ft780_socket_connect(int32_t socket,
                                      DRV_NET_SOCKET_TYPE service_type,
                                      char *pstr_ip,
                                      char *pstr_port,
                                      uint32_t fifo_size) {
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    char pbuf[64];
    int32_t len;
    uint32_t i;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket");
        return NET_ERR_UNKOWN;
    }
    if ((service_type != DRV_NET_SOCKET_TCP) && (service_type != DRV_NET_SOCKET_UDP)) {
        NET_ERR("invalid service type%d", service_type);
        return NET_ERR_UNKOWN;
    }
    if ((pstr_ip == NULL) || (pstr_port == NULL)) {
        NET_ERR("");
        return NET_ERR_UNKOWN;
    }
    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[socket].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("unknown");
        return ret;
    }
    //+ZIPOPEN=<Socketid>,<Type>, <Remote IP>,<Remote port>[,< Local port>]
    //AT+ZIPOPEN=1,1,59.174.243.135,161\r
    len = snprintf(pbuf, sizeof(pbuf), "AT+ZIPOPEN=%d,%d,%s,%s\r", socket + 1, service_type, pstr_ip, pstr_port);
    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }

    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(2000, 2, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }

    for (i = 20; i > 0; i--) { //wait for 20s
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_SOCKET_OPEN_OK) {
                if (msg_at.value == socket) {
                    break;
                }
            }
        } else if (i == 1) { //loop end
            NET_ERR("open socket %d timeout", socket);
            return NET_ERR_UNKOWN;
        } //else {continue;}
    }
    return NET_ERR_OK;
}

//+CME ERROR: 6003
NET_ERR_CODE drv_ft780_socket_send(int32_t socket, uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t pbuf_len;
    uint32_t i;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    if (pdata == NULL) {
        NET_ERR("null pointer");
        return NET_ERR_UNKOWN;
    }
    pbuf = pvPortMalloc((len << 1) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to malloc send buffer");
        return NET_ERR_UNKOWN;
    }
    pbuf_len = snprintf((char *)pbuf, 64, "AT+ZIPSEND=%d,%d,\"", socket + 1, len);
    if (pbuf_len < 0) {
        NET_ERR("format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    len = base16_encode(pdata, len, pbuf + pbuf_len, (len << 1));
    if (len == 0) {
        NET_ERR("base16 encode");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    pbuf_len += len;
    memcpy(pbuf + pbuf_len, CSTRING("\"\r"));
    pbuf_len += (sizeof("\"\r") - 1);

    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(10000, 1, pbuf, pbuf_len);

    if (ret < 0) {
        NET_ERR("failed to send");
        vPortFree(pbuf);
        return ret;
    }
    for (i = 10; i > 0; i--) { //wait for 10s
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_SOCKET_SEND_OK) {
                if (msg_at.value == socket) {
                    break;
                }
            }
        } else if (i == 1) { //loop end
            NET_ERR("socket %d data transmission timeout", socket);
            vPortFree(pbuf);
            return NET_ERR_UNKOWN;
        } //else {continue;}
    }
    vPortFree(pbuf);
    return NET_ERR_OK;
}

//+ZIPCLOSE =<Socket id>
NET_ERR_CODE drv_ft780_socket_disconnect(int32_t socket) {
    char pbuf[64];
    int32_t len;
    NET_ERR_CODE ret;
    uint32_t i;
    MSG_AT msg_at;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("");
        return NET_ERR_UNKOWN;
    }
    socket = socket + 1;
    len = snprintf(pbuf, sizeof(pbuf), "AT+ZIPCLOSE=%d\r", socket);
    if (len < 0) {
        NET_ERR("format");
        return NET_ERR_UNKOWN;
    }

    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(500, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }

    for (i = 20; i > 0; i--) { //wait for 20s
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_SOCKET_CLOSE_OK) {
                if (msg_at.value == socket) {
                    break;
                }
            }
        } else if (i == 1) { //loop end
            NET_ERR("close socket %d timeout", socket);
            return NET_ERR_UNKOWN;
        } //else {continue;}
    }
    return NET_ERR_OK;
}


NET_ERR_CODE drv_ft780_try_to_sleep(void) {
    uint32_t i;
    for (i = 30; i > 0; i--) {
        NET_INF("waiting for sleeping%d...", i);
        vTaskDelay(1000);
    }
    return NET_ERR_OK;
}


NET_ERR_CODE drv_ft780_try_to_wake_up(void) {
    NET_ERR_CODE ret;
    NET_INF("wake up...");
    ret = drv_net_write_bytes_raw(1000, 5, TSTRING(uint8_t *,"AT\r"));
    if (ret < 0) {
        NET_ERR("failed to wake up ft780 from psm");
        return ret;
    }
    return NET_ERR_OK;
}

NET_ERR_CODE drv_ft780_power_set(NET_POWER_CONTROL net_power) {
    if (net_power == NET_POWER_ON) {
        env_net.pat_handlers = drv_ft780_at_handlers;
        return drv_ft780_try_to_get_ip();
    } else if (net_power == NET_POWER_OFF) {
        drv_net_power_off();
        return NET_ERR_OK;
    } else if (net_power == NET_POWER_SLEEP) {
        return drv_ft780_try_to_sleep();
    } else if (net_power == NET_POWER_WAKE) {
        return drv_ft780_try_to_wake_up();
    } else {
        NET_ERR("invalid power state");
        return NET_ERR_UNKOWN;
    }
}

//at+znvset="update_reg",0
//China Mobile connect
NET_ERR_CODE drv_ft780_cm_connect(char *pstr_ip, char *pstr_port, uint32_t fifo_size) {
    return drv_net_cm_connect(NULL, NULL, fifo_size, CM_FLAG_LIFETIME);
}

NET_ERR_CODE drv_ft780_cm_send(uint8_t *pdata, uint32_t len) {
    return drv_net_cm_send(pdata, len, CM_FLAG_WITH_QUOTATION);
}

NET_ERR_CODE drv_ft780_cm_disconnect(void) {
    return drv_net_cm_disconnect();
}


//use imsi
NET_ERR_CODE drv_ft780_ct_connect(char *pstr_ip, char *pstr_port, uint32_t fifo_size) {
    NET_ERR_CODE ret;
    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[0].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("unknown");
        return ret;
    }
    //init
    //AT+ZNVSET="100012DC",1,"FF"
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+ZNVSET=\"100012DC\",1,\"FF\"\r"));
    if (ret < 0) {
        NET_ERR("");
        return NET_ERR_UNKOWN;
    }
    //AT+ICAS?
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+ICAS?\r"));
    if (ret < 0) {
        NET_ERR("");
        return NET_ERR_UNKOWN;
    }

    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+ZNVGET=\"100012DC\",1\r"));
    if (ret < 0) {
        NET_ERR("");
        return NET_ERR_UNKOWN;
    }

    vTaskDelay(4000);
//
//  ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+ZNVSET=\"10001130\",1,\"FF\"\r"));
//  if (ret < 0) {
//      NET_ERR("");
//      return NET_ERR_UNKOWN;
//  }
    return NET_ERR_OK;
}

//AT+SAD=0023
NET_ERR_CODE drv_ft780_ct_send(uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t pbuf_len;
    NET_ERR_CODE ret;
    if (pdata == NULL) {
        NET_ERR("null pointer");
        return NET_ERR_UNKOWN;
    }
    pbuf = pvPortMalloc(BASE16_ENCODE_LEN(BASE64_ENCODE_LEN(len)) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to alloc");
        return NET_ERR_UNKOWN;
    }
    pbuf_len = snprintf((char *)pbuf, 64, "AT+SAD=00%02X", BASE64_ENCODE_LEN(len));
    if (pbuf_len <= 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    len = base16_base64_encode(pdata, len, pbuf + pbuf_len, BASE16_ENCODE_LEN(BASE64_ENCODE_LEN(len)));
    if (len == 0) {
        NET_ERR("failed to encode by base16_base64");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    pbuf_len += len;
    memcpy(pbuf + pbuf_len, CSTRING("\r"));
    pbuf_len += (sizeof("\r") - 1);

    ret = drv_net_write_bytes_raw(2000, 1, pbuf, pbuf_len);
    if (ret < 0) {
        NET_ERR("");
        vPortFree(pbuf);
        return ret;
    }
    vPortFree(pbuf);
    return NET_ERR_OK;
}


NET_ERR_CODE drv_ft780_ct_close(void) {
//  NET_INF("don't need to close");
    //AT+ZNVGET="1000130",1
//  drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+ZNVGET=\"10001130\",1\r"));
    vTaskSuspendAll();
    if (drv_ft780_ct_recv_buffer_deinit()) {
        NET_ERR("failed to deinit recv buffer for ct");
        xTaskResumeAll();
        return NET_ERR_UNKOWN;
    }
    xTaskResumeAll();
    return NET_ERR_OK;
}

NET_ERR_CODE drv_ft780_cmd_sq_get(uint32_t *psq) {
    NET_ERR_CODE ret;
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+ZESQ?\r"));
    if (ret < 0) {
        *psq = 0;
        return ret;
    }
    *psq = env_net.sq;
    return NET_ERR_OK;
}

NET_ERR_CODE drv_ft780_executor(NET_CMD_CODE cmd, void *pdata) {
    if (cmd == NET_CMD_SQ_GET) {
        return drv_ft780_cmd_sq_get((uint32_t *)pdata);
    } else if (cmd == NET_CMD_CT_ID_TYPE_GET){
        *((NET_CT_ID_TYPE *)pdata) = NET_CT_ID_IMSI;
        return NET_ERR_OK;
    } else {
        return NET_ERR_UNKOWN;
    }
}

const DRV_NET_INTERFACE drv_ft780_interface = {
    .pf_power_set = drv_ft780_power_set,
    .pf_executor = drv_ft780_executor,
    //ct
    .pf_ct_connect = drv_ft780_ct_connect,
    .pf_ct_send = drv_ft780_ct_send,
    .pf_ct_disconnect = drv_ft780_ct_close,
    //cm
    .pf_cm_connect = drv_ft780_cm_connect,
    .pf_cm_send = drv_ft780_cm_send,
    .pf_cm_disconnect = drv_ft780_cm_disconnect,
    //socket
    .pf_socket_connect = drv_ft780_socket_connect,
    .pf_socket_send = drv_ft780_socket_send,
    .pf_socket_disconnect = drv_ft780_socket_disconnect
};


